package gang.org.springframework.framework.metadata;

import gang.org.springframework.framework.core.annotation.GangAnnotationFilter;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author gang.chen
 * @since  2021/6/30 13:36
 */
final public class GangAnnotationTypeMappings {

    private final GangRepeatableContainers repeatableContainers;

    private final GangAnnotationFilter filter;

    private final List<GangAnnotationTypeMapping> mappings;

    private static final Map<GangAnnotationFilter, GangCache> standardRepeatableCache = new ConcurrentHashMap<>();


    /**
     * 业务意义
     * 1）创建AnnotationTypeMappings实例，一个实例表示一个注解类E.g Configuration
     * 2）AnnotationTypeMappings包含多个AnnotationTypeMapping实例，
     * 3）Configuration={Configuration, Component, Indexed}
     * */
    public GangAnnotationTypeMappings(GangRepeatableContainers repeatableContainers,
                                      GangAnnotationFilter filter,
                                      Class<? extends Annotation> annotationType) {
        this.repeatableContainers = repeatableContainers;
        this.filter = filter;
        //集合初始化
        this.mappings = new ArrayList<>();
        addAllMapping(annotationType);
    }

    /**
     * TODO
     * 业务意义？
     **/
    static GangAnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType){
        return forAnnotationType(annotationType, GangAnnotationFilter.PLAIN);
    }


    /**
     * TODO
     * 业务意义？
     * */
    static GangAnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType,
                                                        GangAnnotationFilter annotationFilter){
        return forAnnotationType(annotationType,GangRepeatableContainers.standardRepeatable(), annotationFilter);
    }

    /**
     * TODO
     * 业务意义?
     * */
    static GangAnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType,
                                                        GangRepeatableContainers repeatableContainers,
                                                        GangAnnotationFilter annotationFilter) {

        /**
         *
         * TODO 业务意义 场景一
         * 解析注解的包含关系E.g
         * @Configuration >@Component >@Index(注：不是继承关系)
         * */
        if (repeatableContainers == GangRepeatableContainers.standardRepeatable()) {

            GangCache gangCache = standardRepeatableCache.computeIfAbsent(annotationFilter, key -> new GangCache(repeatableContainers, key));

            GangAnnotationTypeMappings mappings = gangCache.get(annotationType);

            return mappings;
        }

        /**
         *
         * TODO 业务意义 场景二
         * */

        /**
         *
         * TODO 业务意义 场景三
         * */
        return new GangAnnotationTypeMappings(repeatableContainers,annotationFilter,annotationType);
    }

    /**
     * 业务意义，原注解Configuration的映射注解{Component, Indexed}
     * @param annotationType 注解对象E.g Configuration
     * */
    private void addAllMapping(Class<? extends Annotation> annotationType)
    {
        Deque<GangAnnotationTypeMapping> queue = new ArrayDeque<>();
        addIfPossible(queue,null,annotationType,null);

        /**
         * queue非空循环
         * */
        while (!queue.isEmpty())
        {
            GangAnnotationTypeMapping mapping = queue.removeFirst();
            /**
             * 1）mappings是一个List集合
             * 2）mappings存储映射关系为Configuration={Configuration, Component, Indexed}
             * 3）存入注解Configuration本身
             * */
            this.mappings.add(mapping);

            /**
             * TODO
             * 控制queue的非空，循环mappings加入
             * */
            addMetaAnnotationsToQueue(queue, mapping);
        }
    }


    /**
     * TODO 业务意义
     * @param queue 队列
     * @param source 原注解，业务意义：Configuration（原注解），{Component, Indexed}(映射注解)
     * */
    private void addMetaAnnotationsToQueue(Deque<GangAnnotationTypeMapping> queue, GangAnnotationTypeMapping source){

        Annotation[] metaAnnotations = GangAnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
        for (Annotation metaAnnotation : metaAnnotations) {

            boolean mappable = isMappable(source, metaAnnotation);
            //场景一
            if( !mappable ){
                continue;
            }
            //场景二
            if ( 1 == 2 ){
                //TODO
            }else {
                addIfPossible(queue,source,metaAnnotation);
            }
        }
    }

    /**
     * TODO
     * */
    private void addIfPossible(Deque<GangAnnotationTypeMapping> queue,
                               GangAnnotationTypeMapping source,
                               Annotation annotation){
        addIfPossible(queue, source, annotation.annotationType(), annotation);
    }

    //TODO
    private void addIfPossible(Deque<GangAnnotationTypeMapping> queue,
                               GangAnnotationTypeMapping source,
                               Class<? extends Annotation> annotationType,
                               Annotation ann)
    {
        queue.addLast(new GangAnnotationTypeMapping(source,annotationType,ann));
    }

    int size ()
    {
        return this.mappings.size();
    }

    GangAnnotationTypeMapping get(int index)
    {
        return this.mappings.get(index);
    }


    /**
     * TODO 业务意义？
     *
     * */
    private static class GangCache {

        private final GangRepeatableContainers repeatableContainers;

        private final GangAnnotationFilter filter;

        private final Map<Class<? extends Annotation>, GangAnnotationTypeMappings> mappings;

        public GangCache(GangRepeatableContainers repeatableContainers, GangAnnotationFilter filter, Map<Class<? extends Annotation>, GangAnnotationTypeMappings> mappings) {
            this.repeatableContainers = repeatableContainers;
            this.filter = filter;
            this.mappings = mappings;
        }

        public GangCache(GangRepeatableContainers repeatableContainers, GangAnnotationFilter filter){
            this.repeatableContainers = repeatableContainers;
            this.filter = filter;
            this.mappings = new ConcurrentHashMap<>();
        }

        /**
         * 业务意义
         * 1）创建AnnotationTypeMappings单个实例
         * 2）单个AnnotationTypeMappings包含一个集合List<AnnotationTypeMapping>
         * */
        GangAnnotationTypeMappings get(Class<? extends  Annotation> annotationType){
            return this.mappings.computeIfAbsent(annotationType, this::createMappings);
        }

        GangAnnotationTypeMappings createMappings(Class<? extends  Annotation> annotationType){

            GangAnnotationTypeMappings mappings = new GangAnnotationTypeMappings(this.repeatableContainers, this.filter, annotationType);

            return mappings;
        }
    }

    /**
     *业务意义
     * 1）过滤器执行
     * 2）非空判断
     * @param source 原注解 E.g Configuration
     * @param annotation 原注解映射注解 {Component, Indexed}
     * */
    private boolean isMappable(GangAnnotationTypeMapping source, Annotation annotation){

        //++++++++++++++++++++++++++++++++++++to do++++++++++++++++++++++++++++++++++++
        if (annotation == null)
        {
            return false;
        }else {
            return true;
        }


        //++++++++++++++++++++++++++++++++++++update++++++++++++++++++++++++++++++++++++
        /*if (this.filter == null){
            return false;
        }
        return annotation != null &&
                !this.filter.matches(annotation) &&
                !GangAnnotationFilter.PLAIN.matches(source.getAnnotationType())&&
                !isAlreadyMapped(source, annotation);*/
    }

    /**
     * @param source 原注解  Configuration
     * @param annotation 映射注解 {Component, Index}
     * */
    private boolean isAlreadyMapped(GangAnnotationTypeMapping source, Annotation annotation){
        Class<? extends Annotation> annotationType = annotation.annotationType();
        GangAnnotationTypeMapping mapping = source;
        while (mapping != null){
            if (mapping.getAnnotationType() == annotationType) {
                return true;
            }
            mapping = mapping.getSource();
        }
        return false;
    }
}
